#include <stdlib.h>
#include <string.h>
#include "filelist.h"

#define FILE_ALLOC 100

static int allocate (filelist_t *filelist);
static int search (filelist_t *filelist, const char *name, size_t namelen, off_t size, time_t mtime, unsigned int *position);

void filelist_init (filelist_t *filelist, eSortCriteria sort_criteria, eSortOrder sort_order)
{
	filelist->records = NULL;
	filelist->index = NULL;

	filelist->size = 0;
	filelist->used = 0;

	filelist->sort_criteria = sort_criteria;
	filelist->sort_order = sort_order;
}

void filelist_free (filelist_t *filelist)
{
	if (filelist->records) {
		free (filelist->records);
		filelist->records = NULL;
	}

	if (filelist->index) {
		free (filelist->index);
		filelist->index = NULL;
	}

	filelist->size = 0;
	filelist->used = 0;
}

int allocate (filelist_t *filelist)
{
	file_t *records;
	off_t *index;
	size_t size;

	if (filelist->used == filelist->size) {
		size = filelist->size + FILE_ALLOC;
		index = (off_t *) malloc (size * sizeof (off_t));
		if (!index) {
			return -1;
		}

		records = (file_t *) realloc (filelist->records, size * sizeof (file_t));
		if (!records) {
			free (index);
			return -1;
		}

		if (filelist->index) {
			memcpy (index, filelist->index, filelist->used * sizeof (off_t));
			free (filelist->index);
		}

		filelist->records = records;
		filelist->index = index;
		filelist->size = size;
	}

	return 0;
}

int filelist_insert (filelist_t *filelist, const char *name, size_t namelen, off_t size, time_t mtime)
{
	unsigned int position;
	file_t *record;

	search (filelist, name, namelen, size, mtime, &position);

	if (allocate (filelist) < 0) {
		return -1;
	}

	record = &(filelist->records[filelist->used]);

	memcpy (record->name, name, namelen);
	record->name[namelen] = 0;

	record->namelen = namelen;

	record->size = size;
	record->mtime = mtime;

	if (position < filelist->used) {
		memmove (&(filelist->index[position + 1]), &(filelist->index[position]), (filelist->used - position) * sizeof (off_t));
	}

	filelist->index[position] = filelist->used++;

	return 0;
}

int search (filelist_t *filelist, const char *name, size_t namelen, off_t size, time_t mtime, unsigned int *position)
{
	file_t *record;
	int i, j, pivot;
	int ret;

	i = 0;
	j = filelist->used - 1;

	while (i <= j) {
		pivot = (i + j) / 2;
		record = &(filelist->records[filelist->index[pivot]]);

		switch (filelist->sort_criteria) {
			case SORT_BY_NAME:
				ret = strcmp (name, record->name);
				break;
			case SORT_BY_NAMELEN:
				if (namelen < record->namelen) {
					ret = -1;
				} else if (namelen == record->namelen) {
					ret = 0;
				} else {
					ret = 1;
				}

				break;
			case SORT_BY_SIZE:
				if (size < record->size) {
					ret = -1;
				} else if (size == record->size) {
					ret = 0;
				} else {
					ret = 1;
				}

				break;
			default:
				/* SORT_BY_DATE */
				if (mtime < record->mtime) {
					ret = -1;
				} else if (mtime == record->mtime) {
					ret = 0;
				} else {
					ret = 1;
				}
		}

		ret *= (int) filelist->sort_order;

		if (ret < 0) {
			j = pivot - 1;
		} else if (ret == 0) {
			*position = (unsigned int) pivot;
			return 0;
		} else {
			i = pivot + 1;
		}
	}

	*position = (unsigned int) i;

	return -1;
}
